package com.hy.service.impl;


import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hy.mapper.MedicineBasicInfoMapper;
import com.hy.mapper.MedicineStockMapper;
import com.hy.mapper.MedicineStockRecordMapper;
import com.hy.mapper.UserMapper;
import com.hy.medicine.MedicineBasicInfo;
import com.hy.medicine.MedicineStock;
import com.hy.medicine.constant.StockConstant;
import com.hy.medicine.dto.StockQueryDTO;
import com.hy.medicine.vo.MedicineStockRecordVO;
import com.hy.medicine.vo.MedicineStockVO;
import com.hy.service.MedicineStockService;
import com.hy.util.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hy.medicine.MedicineStockRecord;

import com.hy.medicine.dto.StockOperateDTO;
import com.hy.medicine.dto.StockRecordQueryDTO;


import lombok.extern.slf4j.Slf4j;


import java.util.*;
import java.util.stream.Collectors;


/**
 * @author Lenovo
 * @description 针对表【medicine_stock(药品库存表)】的数据库操作Service实现
 * @createDate 2025-03-11 19:16:10
 */
@Service
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class MedicineStockServiceImpl extends ServiceImpl<MedicineStockMapper, MedicineStock>
    implements MedicineStockService {

    @Autowired
    private MedicineStockMapper stockMapper;

    @Autowired
    private MedicineStockRecordMapper recordMapper;



    @Value("${stock.warning.threshold:100}")
    private Integer warningThreshold;

    /**
     * 库存预警阈值，默认为100
     */
//    @Value("${stock.warning.threshold:100}")
//    private Integer warningThreshold;

    /**
     * 处理药品入库操作
     *
     * @param dto 包含药品入库信息的StockOperateDTO对象
     * @return 返回一个Result对象，表示入库操作的结果
     *
     * 该方法用于处理药品入库操作，并在一个事务中执行，确保数据一致性。
     * 如果在执行过程中发生任何异常，事务将回滚。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result stockIn(StockOperateDTO dto) {
        // 1. 查询当前库存
        MedicineStock stock = stockMapper.selectByMedicineId(dto.getMedicineId());
        if (stock == null) {
            // 如果库存记录不存在，创建一个新的库存记录
            stock = new MedicineStock();
            stock.setMedicineId(dto.getMedicineId()); // 设置药品ID
            stock.setStockQuantity(dto.getQuantity()); // 设置库存数量为入库数量
            stock.setInDate(new Date()); // 设置入库日期为当前日期
            stockMapper.insert(stock); // 将新的库存记录插入数据库
        } else {
            // 如果库存记录存在，更新库存数量和入库日期
            stock.setStockQuantity(stock.getStockQuantity() + dto.getQuantity()); // 更新库存数量
            stock.setInDate(new Date()); // 更新入库日期为当前日期
            stockMapper.updateById(stock); // 更新库存记录到数据库
        }
        // 2. 记录变动
        saveStockRecord(dto, stock, 1); // 记录本次入库操作的变动信息
        return Result.success(); // 返回操作成功的Result对象
    }

    /**
     * 处理药品出库操作
     *
     * @param dto 包含药品出库信息的StockOperateDTO对象
     * @return 返回一个Result对象，表示出库操作的结果
     *
     * 该方法用于处理药品出库操作，并在一个事务中执行，确保数据一致性。
     * 如果在执行过程中发生任何异常，事务将回滚。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result stockOut(StockOperateDTO dto) {
        MedicineStock stock = stockMapper.selectByMedicineId(dto.getMedicineId());
        if (stock == null || stock.getStockQuantity() < dto.getQuantity()) {
            return Result.error("库存不足"); // 如果库存记录不存在或库存数量不足，返回错误
        }

        stock.setStockQuantity(stock.getStockQuantity() - dto.getQuantity()); // 更新库存数量
        stock.setOutDate(new Date()); // 设置出库日期为当前日期
        stockMapper.updateById(stock); // 更新库存记录到数据库

        saveStockRecord(dto, stock, 2); // 记录本次出库操作的变动信息
        return Result.success(); // 返回操作成功的Result对象
    }

    /**
     * 调整药品库存
     *
     * @param dto 包含药品库存调整信息的StockOperateDTO对象
     * @return 返回一个Result对象，表示库存调整操作的结果
     *
     * 该方法用于处理药品库存调整操作，并在一个事务中执行，确保数据一致性。
     * 如果在执行过程中发生任何异常，事务将回滚。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result adjustStock(StockOperateDTO dto) {
        MedicineStock stock = stockMapper.selectByMedicineId(dto.getMedicineId());
        if (stock == null) {
            return Result.error("药品库存不存在"); // 如果库存记录不存在，返回错误
        }

        Integer oldQuantity = stock.getStockQuantity(); // 获取旧的库存数量
        stock.setStockQuantity(dto.getQuantity()); // 设置库存数量为调整后的数量
        stockMapper.updateById(stock); // 更新库存记录到数据库

        // 记录调整
        MedicineStockRecord record = new MedicineStockRecord();
        record.setMedicineId(dto.getMedicineId()); // 设置药品ID
        record.setChangeQuantity(dto.getQuantity() - oldQuantity); // 设置变动数量
        record.setBeforeQuantity(oldQuantity); // 设置变动前的数量
        record.setAfterQuantity(dto.getQuantity()); // 设置变动后的数量
        record.setChangeType(3); // 设置变动类型为调整
        record.setOperatorId(dto.getOperatorId()); // 设置操作人ID
        record.setRemark(dto.getRemark()); // 设置备注
        record.setOperateTime(new Date()); // 设置操作时间
        recordMapper.insert(record); // 将变动记录插入数据库

        return Result.success(); // 返回操作成功的Result对象
    }

    /**
     * 获取库存变动记录
     *
     * @param dto 包含查询条件的StockRecordQueryDTO对象
     * @return 返回一个Result对象，包含库存变动记录的分页结果
     *
     * 该方法用于根据查询条件获取库存变动记录，并返回分页结果。
     */
    @Override
    public Result getStockRecords(StockRecordQueryDTO dto) {
        try {
            Page<MedicineStockRecordVO> page = new Page<>(dto.getPageNum(), dto.getPageSize()); // 创建分页对象
            Page<MedicineStockRecordVO> result = recordMapper.selectStockRecords(page, dto); // 查询库存变动记录
            return Result.success(result); // 返回查询成功的Result对象
        } catch (Exception e) {
            log.error("查询库存变动记录失败", e); // 记录错误日志
            return Result.error("查询库存变动记录失败"); // 返回查询失败的Result对象
        }
    }

    /**
     * 获取库存统计信息
     *
     * @return 返回一个Result对象，包含库存统计信息
     *
     * 该方法用于获取库存的统计信息，包括总库存数量、库存预警数量、本月入库数量和本月出库数量。
     */
    @Override
    public Result getStockStats() {
        Map<String, Object> stats = new HashMap<>();

        // 总库存数量
        stats.put("totalQuantity", stockMapper.sumTotalQuantity()); // 计算并设置总库存数量

        // 库存预警数量
        stats.put("warningCount", stockMapper.countWarning()); // 计算并设置库存预警数量

        // 本月入库数量
        stats.put("monthlyIn", recordMapper.sumMonthlyIn()); // 计算并设置本月入库数量

        // 本月出库数量
        stats.put("monthlyOut", recordMapper.sumMonthlyOut()); // 计算并设置本月出库数量

        return Result.success(stats); // 返回统计信息成功的Result对象
    }

    /**
     * 获取库存列表
     *
     * @param queryDTO 包含查询条件的StockQueryDTO对象
     * @return 返回一个Result对象，包含库存列表的分页结果
     *
     * 该方法用于根据查询条件获取库存列表，并返回分页结果。
     */
    @Override
    public Result getStockList(StockQueryDTO queryDTO) {
        try {
            Page<MedicineStockVO> page = new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize()); // 创建分页对象
            Page<MedicineStockVO> result = stockMapper.selectStockRecords(page, queryDTO); // 查询库存列表
            return Result.success(result); // 返回查询成功的Result对象
        } catch (Exception e) {
            log.error("查询库存变动记录失败", e); // 记录错误日志
            return Result.error("查询库存变动记录失败"); // 返回查询失败的Result对象
        }
    }

    /**
     * 批量处理药品入库操作
     *
     * @param dtoList 包含多个药品入库信息的StockOperateDTO对象列表
     * @return 返回一个Result对象，表示批量入库操作的结果
     *
     * 该方法用于处理多个药品的入库操作，并在一个事务中执行，确保数据一致性。
     * 如果在执行过程中发生任何异常，事务将回滚。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result batchStockIn(List<StockOperateDTO> dtoList) {
        try {
            if (CollectionUtils.isEmpty(dtoList)) {
                return Result.error("入库数据不能为空"); // 如果入库数据列表为空，返回错误
            }

            // 1. 批量查询现有库存
            List<Integer> medicineIds = dtoList.stream()
                    .map(StockOperateDTO::getMedicineId) // 提取药品ID
                    .collect(Collectors.toList()); // 收集到列表中

            List<MedicineStock> existingStocks = stockMapper.selectList(
                    new LambdaQueryWrapper<MedicineStock>()
                            .in(MedicineStock::getMedicineId, medicineIds) // 查询存在的库存记录
            );

            Map<Integer, MedicineStock> stockMap = existingStocks.stream()
                    .collect(Collectors.toMap(MedicineStock::getMedicineId, stock -> stock)); // 将库存记录转换为Map

            // 2. 准备批量操作的数据
            List<MedicineStock> insertList = new ArrayList<>();
            List<MedicineStock> updateList = new ArrayList<>();
            List<MedicineStockRecord> recordList = new ArrayList<>();
            Date now = new Date(); // 获取当前日期

            for (StockOperateDTO dto : dtoList) {
                // 验证数据
                if (dto.getQuantity() <= 0) {
                    throw new IllegalArgumentException("入库数量必须大于0"); // 如果入库数量小于等于0，抛出异常
                }

                MedicineStock stock = stockMap.get(dto.getMedicineId());

                // 处理库存记录
                if (stock == null) {
                    // 新增库存
                    stock = new MedicineStock();
                    stock.setMedicineId(dto.getMedicineId()); // 设置药品ID
                    stock.setStockQuantity(dto.getQuantity()); // 设置库存数量为入库数量
                    stock.setInDate(now); // 设置入库日期为当前日期
                    stock.setWarehouseLocation(dto.getWarehouseLocation()); // 设置仓库位置
                    insertList.add(stock); // 添加到插入列表
                } else {
                    // 更新库存
                    stock.setStockQuantity(stock.getStockQuantity() + dto.getQuantity()); // 更新库存数量
                    stock.setInDate(now); // 设置入库日期为当前日期
                    if (StringUtils.isNotBlank(dto.getWarehouseLocation())) {
                        stock.setWarehouseLocation(dto.getWarehouseLocation()); // 更新仓库位置
                    }
                    updateList.add(stock); // 添加到更新列表
                }

                // 创建变动记录
                MedicineStockRecord record = new MedicineStockRecord();
                record.setMedicineId(dto.getMedicineId()); // 设置药品ID
                record.setChangeQuantity(dto.getQuantity()); // 设置变动数量
                record.setBeforeQuantity(stock.getStockQuantity() - dto.getQuantity()); // 设置变动前的数量
                record.setAfterQuantity(stock.getStockQuantity()); // 设置变动后的数量
                record.setChangeType(StockConstant.CHANGE_TYPE_IN); // 设置变动类型为入库
                record.setOperatorId(dto.getOperatorId()); // 设置操作人ID
                record.setRemark(dto.getRemark()); // 设置备注
                record.setOperateTime(now); // 设置操作时间
                recordList.add(record); // 添加到变动记录列表
            }

            // 3. 执行批量操作
            if (!insertList.isEmpty()) {
                stockMapper.insertBatch(insertList); // 批量插入新的库存记录
            }
            if (!updateList.isEmpty()) {
                stockMapper.updateBatchById(updateList); // 批量更新现有的库存记录
            }
            if (!recordList.isEmpty()) {
                recordMapper.insertBatch(recordList); // 批量插入变动记录
            }

            return Result.success(); // 返回操作成功的Result对象
        } catch (Exception e) {
            log.error("批量入库失败", e); // 记录错误日志
            throw new RuntimeException("批量入库失败: " + e.getMessage()); // 抛出运行时异常
        }
    }

    /**
     * 保存库存变动记录
     *
     * @param dto 包含操作信息的StockOperateDTO对象
     * @param stock 包含库存信息的MedicineStock对象
     * @param changeType 变动类型（1表示入库，2表示出库，3表示调整）
     *
     * 该方法用于根据操作信息保存库存变动记录。
     */
    private void saveStockRecord(StockOperateDTO dto, MedicineStock stock, int changeType) {
        MedicineStockRecord record = new MedicineStockRecord();
        record.setMedicineId(dto.getMedicineId()); // 设置药品ID
        record.setChangeQuantity(changeType == 1 ? dto.getQuantity() : -dto.getQuantity()); // 设置变动数量
        record.setBeforeQuantity(changeType == 1 ?
                stock.getStockQuantity() - dto.getQuantity() : // 设置变动前的数量
                stock.getStockQuantity() + dto.getQuantity());
        record.setAfterQuantity(stock.getStockQuantity()); // 设置变动后的数量
        record.setChangeType(changeType); // 设置变动类型
        record.setOperatorId(dto.getOperatorId()); // 设置操作人ID
        record.setRemark(dto.getRemark()); // 设置备注
        record.setOperateTime(new Date()); // 设置操作时间
        recordMapper.insert(record); // 将变动记录插入数据库
    }
}
